// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//
#include <pollux/testing/gtest_utils.h>
#include <pollux/plan/plan_builder.h>
#include <pollux/functions/lib/aggregates/tests/utils/AggregationTestBase.h>

using namespace kumo::pollux::functions::aggregate::test;
using namespace kumo::pollux::plan;

namespace kumo::pollux::aggregate::test {
    namespace {
        struct TestParams {
            std::string polluxName;
            std::string duckDbName;

            std::string toString() const {
                return polluxName;
            }
        };

        class BoolAndOrTest : public virtual AggregationTestBase,
                              public testing::WithParamInterface<TestParams> {
        protected:
            void SetUp() override {
                AggregationTestBase::SetUp();
            }
        };

        TEST_P(BoolAndOrTest, basic) {
            auto rowType = ROW({"c0", "c1"}, {BIGINT(), BOOLEAN()});
            auto vectors = makeVectors(rowType, 1000, 10);
            createDuckDbTable(vectors);

            const auto polluxName = GetParam().polluxName;
            const auto duckDbName = GetParam().duckDbName;

            const auto partialAgg = fmt::format("{}(c1)", polluxName);

            // Global aggregation.
            testAggregations(
                vectors,
                {},
                {partialAgg},
                fmt::format("SELECT {}(c1::TINYINT) FROM tmp", duckDbName));

            // Group by aggregation.
            testAggregations(
                [&](auto &builder) {
                    builder.values(vectors).project({"c0 % 10", "c1"});
                },
                {"p0"},
                {partialAgg},
                fmt::format(
                    "SELECT c0 % 10, {}(c1::TINYINT) FROM tmp GROUP BY 1", duckDbName));

            // Encodings: use filter to wrap aggregation inputs in a dictionary.
            testAggregations(
                [&](auto &builder) {
                    builder.values(vectors).filter("c0 % 2 = 0").project({"c0 % 11", "c1"});
                },
                {"p0"},
                {partialAgg},
                fmt::format(
                    "SELECT c0 % 11, {}(c1::TINYINT) FROM tmp WHERE c0 % 2 = 0 GROUP BY 1",
                    duckDbName));

            testAggregations(
                [&](auto &builder) { builder.values(vectors).filter("c0 % 2 = 0"); },
                {},
                {partialAgg},
                fmt::format(
                    "SELECT {}(c1::TINYINT) FROM tmp WHERE c0 % 2 = 0", duckDbName));
        }

        POLLUX_INSTANTIATE_TEST_SUITE_P(
            BoolAndOrTest,
            BoolAndOrTest,
            testing::Values(
                TestParams{"bool_and", "bit_and"},
                TestParams{"every", "bit_and"},
                TestParams{"bool_or", "bit_or"}),
            [](auto p) { return p.param.toString(); });
    } // namespace
} // namespace kumo::pollux::aggregate::test
